
#include <iostream>
#include "gameState.h"


// initial board
GameState::GameState()
{
    FieldValue startBoard[8][8] = { 
        BLACK_ROOK, BLACK_KNIGHT, BLACK_BISHOP, BLACK_QUEEN, BLACK_KING, BLACK_BISHOP, BLACK_KNIGHT, BLACK_ROOK,
        BLACK_PAWN, BLACK_PAWN,   BLACK_PAWN,   BLACK_PAWN,  BLACK_PAWN, BLACK_PAWN,   BLACK_PAWN,   BLACK_PAWN,
        EMPTY,      EMPTY,        EMPTY,        EMPTY,       EMPTY,      EMPTY,        EMPTY,        EMPTY, 
        EMPTY,      EMPTY,        EMPTY,        EMPTY,       EMPTY,      EMPTY,        EMPTY,        EMPTY,
        EMPTY,      EMPTY,        EMPTY,        EMPTY,       EMPTY,      EMPTY,        EMPTY,        EMPTY,
        EMPTY,      EMPTY,        EMPTY,        EMPTY,       EMPTY,      EMPTY,        EMPTY,        EMPTY,
        WHITE_PAWN, WHITE_PAWN,   WHITE_PAWN,   WHITE_PAWN,  WHITE_PAWN, WHITE_PAWN,   WHITE_PAWN,   WHITE_PAWN, 
        WHITE_ROOK, WHITE_KNIGHT, WHITE_BISHOP, WHITE_QUEEN, WHITE_KING, WHITE_BISHOP, WHITE_KNIGHT, WHITE_ROOK
    };
    memcpy(board, startBoard, 64*sizeof(int));
    
    whiteLeftCastlePossible = true;
    whiteRightCastlePossible = true;
    blackLeftCastlePossible = true;
    blackRightCastlePossible = true;
    
    lastMoveEnPassantFirstStage = false;
    
    getAttackInfo();
    computeIsUnderCheck();
    //computeIsLosingState();
    
    drawRuleCount = 0;
    
    round = WHITE;  
}
       
 
GameState::GameState(const FieldValue newBoard[8][8], Colour r) 
{
    memcpy(board, newBoard, 64*sizeof(FieldValue));
    round = r;
}
                
// apply a move to the state, and get it
GameState GameState::applyMove(const Move& m) const
{
    // The new state. 
    GameState st = GameState(this->board, this->round);
    
    // Update the board info.
    st.board[m.destination_row][m.destination_col] = st.board[m.source_row][m.source_col];
    st.board[m.source_row][m.source_col] = EMPTY;
    
    st.drawRuleCount = drawRuleCount;
    
    // Update the round info.
    Colour curr = this->round;
    if(curr == WHITE)
        st.round = BLACK;
    else
        st.round = WHITE;
    
    st.lastMoveEnPassantFirstStage = false;
    
    
    // Update the information about castling
    
    if(this->board[m.source_row][m.source_col] != BLACK_KING)
    {
        // check if me moved the left black rook
        if(this->blackLeftCastlePossible && m.source_row == 0 && m.source_col == 0)
            st.blackLeftCastlePossible = false;
        else
            st.blackLeftCastlePossible = this->blackLeftCastlePossible;
        
        // check if me moved the right black rook
        if(this->blackRightCastlePossible && m.source_row == 0 && m.source_col == 7)
            st.blackRightCastlePossible = false;
        else
            st.blackRightCastlePossible = this->blackRightCastlePossible;
    }
    else
    {
        st.blackLeftCastlePossible = false;
        st.blackRightCastlePossible = false;   
    }
    
    if(this->board[m.source_row][m.source_col] != WHITE_KING)
    {
        // check if me moved the left white rook
        if(this->whiteLeftCastlePossible && m.source_row == 7 && m.source_col == 0)
            st.whiteLeftCastlePossible = false;
        else
            st.whiteLeftCastlePossible = this->whiteLeftCastlePossible;
        
        // check if me moved the right white rook
        if(this->whiteRightCastlePossible && m.source_row == 7 && m.source_col == 7)
            st.whiteRightCastlePossible = false;
        else
            st.whiteRightCastlePossible = this->whiteRightCastlePossible;
    }
    else
    {
        st.whiteLeftCastlePossible = false;
        st.whiteRightCastlePossible = false;   
    }
    
    
    if(m.type == PROMOTION_FOR_ROOK && curr == WHITE)
        st.board[m.destination_row][m.destination_col] = WHITE_ROOK;
    else if(m.type == PROMOTION_FOR_ROOK && curr == BLACK)
        st.board[m.destination_row][m.destination_col] = BLACK_ROOK;
    else if(m.type == PROMOTION_FOR_KNIGHT && curr == WHITE)
        st.board[m.destination_row][m.destination_col] = WHITE_KNIGHT;
    else if(m.type == PROMOTION_FOR_KNIGHT && curr == BLACK)
        st.board[m.destination_row][m.destination_col] = BLACK_KNIGHT;
    else if(m.type == PROMOTION_FOR_BISHOP && curr == WHITE)
        st.board[m.destination_row][m.destination_col] = WHITE_BISHOP;
    else if(m.type == PROMOTION_FOR_BISHOP && curr == BLACK)
        st.board[m.destination_row][m.destination_col] = BLACK_BISHOP;
    else if(m.type == PROMOTION_FOR_QUEEN && curr == WHITE)
        st.board[m.destination_row][m.destination_col] = WHITE_QUEEN;
    else if(m.type == PROMOTION_FOR_QUEEN && curr == BLACK)
        st.board[m.destination_row][m.destination_col] = BLACK_QUEEN;
    else if(m.type == ENPASSANT_FIRST_STAGE)
    {
        st.lastMoveEnPassantFirstStage = true;
        st.pawnTwoRow = m.destination_row;
        st.pawnTwoCol = m.destination_col;   
    }
    else if(m.type == ENPASSANT_SECOND_STAGE)
    {
        // Have to delete the opposite pawn from board   
        st.board[this->pawnTwoRow][this->pawnTwoCol] = EMPTY;
    }
    else if(m.type == LEFTSIDE_CASTLING)
    {
        // castling is a king move 
        if(this->round == BLACK)
        {
            // move the left black rook   
            st.board[0][3] = BLACK_ROOK;
            st.board[0][0] = EMPTY;
        }
        else
        {
            // move the left white rook   
            st.board[7][3] = BLACK_ROOK;
            st.board[7][0] = EMPTY;
        }
    }
    else if(m.type == RIGHTSIDE_CASTLING)
    {
        // castling is a king move 
        if(this->round == BLACK)
        {
            // move the right black rook   
            st.board[0][5] = BLACK_ROOK;
            st.board[0][7] = EMPTY;
        }
        else
        {
            // move the right white rook   
            st.board[7][5] = BLACK_ROOK;
            st.board[7][7] = EMPTY;
        }
    }
    
    st.getAttackInfo();
    st.computeIsUnderCheck();
    //st.computeIsLosingState();
    
    if(st.board[m.destination_row][m.destination_col] != BLACK_PAWN && st.board[m.destination_row][m.destination_col] != WHITE_PAWN &&
	m.type != PROMOTION_FOR_ROOK && m.type != PROMOTION_FOR_KNIGHT && m.type !=PROMOTION_FOR_BISHOP && m.type !=PROMOTION_FOR_QUEEN
	&& Util::pieceColour(board[m.destination_row][m.destination_col]) == round)
      st.drawRuleCount++;
    else
        st.drawRuleCount = 0;
    return st;   
}


// Zaklad ze size_t ma 4 bajty ....

size_t GameState::hash() const
{
    // Dziele plansze na 16 kwadratow 2 x 2, kazdy idzie na 2 biy
    static char shift = 2;
    static char sum;
    static int primes[64] =
    {
        31,     37 ,    41,     43 ,    47 ,    53 ,    59 ,    61 ,    67 ,   71 ,
        73 ,    79 ,    83,     89 ,    97 ,    101,    103,    107,    109,   113,
        127,    131 ,   137,    139,    149,    151,    157,    163,    167,   173,
        179,    181,    191,    193,    197,    199,    211,    223,    227,   229,
        233,    239,    241,    251,    257,    263,    269,    271,    277,   281,
        283,    293,    307,    311,    313,    317,    331,    337,    347,   349,
        353,    359,    367,    373  }; 
    
    size_t result = 0;
    
    for(char row = 1; row <= 7; row += shift)
    {
        for(char col = 1; col <= 7; col += shift)   
        {
            sum = 0;
            sum += board[row][col]*primes[row*8 + col];
            sum += board[row][col + 1]*primes[row*8 + col+1];
            sum += board[row + 1][col]*primes[(row+1)*8 + col];
            sum += board[row + 1][col + 1]* primes[(row+1)*8 + col+1];
            
            char x = sum % 4;
        
            result  = result | ( (x & 1)  << 8*(row/2)+(col+1) );
            result  = result | ( (x & 2)  << 8*(row/2)+(col+1) );
        
        } 
    }
    return result;
}


void GameState::computeIsUnderCheck()
{
    isPlayerUnderCheck = false;
    isEnemyUnderCheck = false;
    for(int i = 0; i < 8; i++)
    {
        for(int j = 0; j < 8; j++)
            if(board[i][j] == WHITE_KING && round == WHITE && attackInfo[1][i][j] > 0)
                isPlayerUnderCheck = true;
            else if(board[i][j] == WHITE_KING && round == BLACK && attackInfo[0][i][j] > 0)
                isEnemyUnderCheck = true;
            else if(board[i][j] == BLACK_KING && round == BLACK && attackInfo[1][i][j] > 0)
                isPlayerUnderCheck = true;
            else if(board[i][j] == BLACK_KING && round == WHITE && attackInfo[0][i][j] > 0)
                isEnemyUnderCheck = true;
    }           
}


bool GameState::computeIsLosingState() const
{
	if(!isPlayerUnderCheck)
	{
		return false;
   }
	
	if(generateAllMoves().size() == 0)
	{
		return true;
	}
	return false;
}



